if (typeof inweo.utils.drag == "undefined") {
	inweo.utils.drag = function drag(element) {
		// properties
		var self			= this;
		this.X				= "x";
		this.Y				= "y";
		this.__cancelX		= false;
		this.__cancelY		= false;
		this.__dragStart	= [];
		this.__beforeDrag	= [];
		this.__drag			= [];
		this.__dragEnd		= [];
		this.x				= "left";
		this.y				= "top";
		this.element		= (!element.widget) ? element : element.widget;
		this.__handler		= element;

		// methods
		this.getHandler = function() {
			return this.__handler;
		}
		this.__addEvent = function(element, event, callback) {
			if (element.addEventListener) {
				element.addEventListener(event, callback, false);
			} else if (element.attachEvent) {
				element.attachEvent("on" + event, callback);
			}
		}
		this.__removeEvent = function(element, event, callback) {
			if (element.removeEventListener) {
				element.removeEventListener(event, callback, false);
			} else if (element.detachEvent) {
				element.detachEvent("on" + event, callback);
			}
		}
		this.__getMouseXY = function(event) {
			var x = 0;
			var y = 0;
			if (!event) var event = window.event;
			if (event.pageX || event.pageY) {
				x = event.pageX;
				y = event.pageY;
			} else if (event.clientX || event.clientY) 	{
				x = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
				y = event.clientY + document.body.scrollTop + document.documentElement.scrollTop;
			}
			return {"clientX": x, "clientY": y}
		}
		this.__onMouseDown = function(event) {
			inweo.execute(self.__dragStart, self);
			self.__addEvent(document, "mousemove", self.__onMouseMove);
			self.__addEvent(document, "mouseup", self.__onMouseUp);
		}
		this.__onMouseMove = function(event) {
			var x = parseInt(self.element.style[self.x]);
			var y = parseInt(self.element.style[self.y]);

			event = self.__getMouseXY(event);

			if (typeof self.x == "string") {
				var newX = x + (event.clientX - self.posX);
			} else if (self.x == null) {
				self.__cancelX = true;
			}

			if (typeof self.y == "string") {
				var newY = y + (event.clientY - self.posY);
			} else if (self.y == null) {
				self.__cancelY = true;
			}

			inweo.execute(self.__beforeDrag, self, [x, newX], [y, newY]);

			if (self.__cancelX == true) {
				self.resume(self.X);
				return;
			} else {
				if (!isNaN(newX)) {
					self.element.style[self.x] = newX + "px";
				}
				self.posX = event.clientX;
			}

			if (self.__cancelY == true) {
				self.resume(self.Y);
				return;
			} else {
				if (!isNaN(newY)) {
					self.element.style[self.y] = newY + "px";
				}
				self.posY = event.clientY;
			}

			inweo.execute(self.__drag, self, [self.x, newX], [self.y, newY]);
		}
		this.__onMouseUp = function(event) {
			inweo.execute(self.__dragEnd, self);
			self.__removeEvent(document, "mousemove", self.__onMouseMove);
			self.__removeEvent(document, "mouseup", self.__onMouseUp);
			delete self.posX;
			delete self.posY;
		}
		this.cancel = function(path) {
			if (!path) {
				this.__cancelX = true;
				this.__cancelY = true;
			} else if (path == this.X) {
				this.__cancelX = true;				
			} else if (path == this.Y) {
				this.__cancelY = true;				
			}
		}
		this.resume = function(path) {
			if (!path) {
				this.__cancelX = false;
				this.__cancelY = false;
			} else if (path == this.X) {
				this.__cancelX = false;				
			} else if (path == this.Y) {
				this.__cancelY = false;				
			}
		}
		this.onDragStart = function(fn) {
			this.__dragStart.push(fn);
		}
		this.onBeforeDrag = function(fn) {
			this.__beforeDrag.push(fn);
		}
		this.onDrag = function(fn) {
			this.__drag.push(fn);
		}
		this.onDragEnd = function(fn) {
			this.__dragEnd.push(fn);
		}

		// constructor
		if (isNaN(parseInt(this.element.style.left))) {
			this.element.style.left = "0px";
		}
		if (isNaN(parseInt(this.element.style.top))) {
			this.element.style.top = "0px";
		}
		if (isNaN(parseInt(this.element.style.height))) {
			this.element.style.height = this.element.offsetHeight + "px";
		}
		if (isNaN(parseInt(this.element.style.width))) {
			this.element.style.width = this.element.offsetWidth + "px";
		}

		this.__addEvent(this.element, "mousedown", this.__onMouseDown);
	}
	inweo.utils.drag.handler = function handler(drag, handler) {
		// properties
		var self		= this;
		this.x			= "left";
		this.y			= "top";
		this.drag		= drag;
		this.handler	= handler;

		// methods
		this.modify = function(x, y) {
			// admit it, you have no idea what's this
			this.x = (x == null) ? "opacity" : x;
			this.y = (y == null) ? "opacity" : y;			
		}

		// constructor
		this.drag.__removeEvent(this.drag.element, "mousedown", this.drag.__onMouseDown);
		this.drag.__addEvent(this.handler, "mousedown", function(event) {
			self.drag.__handler		= self.handler;
			self.drag.x				= self.x;
			self.drag.y				= self.y;
			self.drag.__onMouseDown.call(this, event);
		});
	}
}